﻿using System;
using System.Collections.Generic;
using System.Linq;
using Xenta.Collections;
using Xenta.Entities;
using Xenta.Utils;

namespace Xenta.Operations
{
    /// <summary>
    /// Represents the search operation which returns 
    /// the collection of items.
    /// </summary>
    /// <typeparam name="TEntity">The type of entity.</typeparam>
    /// <typeparam name="TItem">The type of items in the result.</typeparam>
    public abstract class SearchOperation<TEntity, TItem> : 
        QueryOperation<TEntity, SearchResult<TItem>>
        where TEntity : IEntity
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        protected SearchOperation()
        {
            Count = Int32.MaxValue;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the entity identifier to search "from".
        /// </summary>
        public int? EntityIDFrom
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the entity identifier to search "to".
        /// </summary>
        public int? EntityIDTo
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the start index.
        /// </summary>
        public int Index
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the number of records to load.
        /// </summary>
        public int Count
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<TEntity> Build(IUnitOfWork uow, IQueryable<TEntity> query)
        {
            query = base.Build(uow, query);
            query = query.FilterByNumRange(x => x.EntityID, EntityIDFrom, EntityIDTo);
            return query;
        }

        /// <summary>
        /// Maps the entity query to result query.
        /// </summary>
        /// <param name="query">The entity query.</param>
        /// <returns>The result query.</returns>
        protected abstract IQueryable<TItem> Map(IQueryable<TEntity> query);

        protected override SearchResult<TItem> Do(IUnitOfWork uow, IQueryable<TEntity> query)
        {
            var map = Map(query);
            long total = 0;
            var items = Enumerable.Empty<TItem>();

            if(Index == 0 && Count == Int32.MaxValue)
            {
                items = map.ToList();
                total = items.LongCount();
            }
            else
            {
                total = map.LongCount();
                if(total != 0)
                    items = map.Paged(Index, Count).ToList();
            }
            return new SearchResult<TItem>
            {
                Items = items,
                TotalCount = total
            };
        }

        #endregion
    }

    /// <summary>
    /// Represents the search operation which returns 
    /// the collection of entities.
    /// </summary>
    /// <typeparam name="TEntity">The type of entity.</typeparam>
    public abstract class SearchOperation<TEntity> : 
        SearchOperation<TEntity, TEntity>
        where TEntity : IEntity
    {
        protected override IQueryable<TEntity> Map(IQueryable<TEntity> query)
        {
            return query;
        }
    }

    /// <summary>
    /// Represents the search operation which returns 
    /// the collection of key-value pairs.
    /// </summary>
    /// <typeparam name="TEntity">The type of entity.</typeparam>
    /// <typeparam name="TKey">The type of key.</typeparam>
    /// <typeparam name="TValue">The type of value.</typeparam>
    public abstract class SearchOperation<TEntity, TKey, TValue> : 
        SearchOperation<TEntity, KeyValuePair<TKey, TValue>>
        where TEntity : IEntity
    {
    }
}
